﻿using System;
using System.Collections.Generic;
using System.Data;
using MySql.Data.MySqlClient;
using PhantomDb;   

namespace CapaDatos
{
    public static class Names
    {
        public const string DbSisCont = "siscont";
        public const string DbSisContTrans = "siscont-t";
        public const string DbSisMedic = "sismedic";
    }

    public static class Datos
    {
        //CONEXION CON MYSQL
        #region Conexiones
#if DEBUG
        private static string connectionString =
        "Server=localhost;Database=siscont;Uid=root;Pwd=;";
#else
        private static string connectionString = 
            "Server=localhost;Database=wi081624_americo;Uid=wi081624_americo;Pwd=cLr12354;";
#endif
        #endregion

        /// <summary>
        /// Instancia de manejador. Permite gestionar múltiples bases de datos, 
        /// soportando tareas multi-hilo.
        /// </summary>
        /// <remarks>
        /// Intentar optimizar el multi-hilo, para que en vez de ser uno-escribe
        /// uno-lee, sea uno-escribe varios-leen. Esto aprobecharía mucho las 
        /// ventajas de los servidores con múltiples núcleos.
        /// </remarks>
        /// <example>
        /// Uso:
        /// <code>
        /// using Entidades;
        /// using CentroMedico.Entidades;
        /// 
        /// // Lista todos los asientos de una base de datos
        /// var asientos = Datos.Manejador[typeof(Entity_asientos)].Listar();
        /// // Y todos los profesionales de otra base de datos
        /// var profs = Datos.Manejador[typeof(Sismedic_Profesionales)].Listar();
        /// </code>
        /// </example>
        public static PhantomDb.PDBMHelper Manejador
        {
            get
            {
                AsignarBaseDeDatos();
                return PhantomDb.PDBMHelper.Instance;
            }
        }

        /// <summary>
        /// Obtiene una base de datos, para poder realizar una consulta.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static DataBaseHandler GetDb(string name)
        {
            try
            {
                return Manejador[name].DataBase;
            }
            catch(NullReferenceException)
            {
                Datos.AsignarBaseDeDatos();
                return Manejador[name].DataBase;
            }
        }

        /// <summary>
        /// Registra las bases de datos disponibles dentro del manejador. En caso
        /// de problemas toma como base de datos defecto a "siscont".
        /// </summary>
        public static void AsignarBaseDeDatos()
        {
            lock (connectionString)
            {
                var handler = PhantomDb.PDBMHelper.Instance;
                // Base de datos defecto: siscont
                handler.DefaultDataBase = Names.DbSisCont;
                // Manejador de siscont
                AgregarManejador(Names.DbSisCont);
                // Manejador de siscont-transaccional
                AgregarManejadorTransaccional(Names.DbSisContTrans);
            }
        }

        /// <summary>
        /// Agrega un manejador en caso de que no exista.
        /// </summary>
        /// <param name="dbName"></param>
        private static void AgregarManejador(string dbName)
        {
            var handler = PhantomDb.PDBMHelper.Instance;
            if (!handler.ExisteManejador(dbName))
                handler.AgregarManejador(dbName, new PhantomDb.DataBaseHandler(GetConnectionString(dbName), Datos.ConectarConsultar));
        }

        /// <summary>
        /// Agrega un manejador en caso de que no exista.
        /// </summary>
        /// <param name="dbName"></param>
        private static void AgregarManejadorTransaccional(string dbName)
        {
            var handler = PhantomDb.PDBMHelper.Instance;
            if (!handler.ExisteManejador(dbName))
                handler.AgregarManejador(dbName, new PhantomDb.DataBaseHandler(GetConnectionString(dbName), Datos.Transaccion));
        }

        /// <summary>
        /// Manejador de bases de datos simple. [DEPRECATED]
        /// Mudar al majenador múltiple.
        /// </summary>
        public static PhantomDb.PDBHelperAbstract Phantom
        {
            get
            {
                if(PhantomDb.PDBHelper.Factory.Consultar == null)
                {
                    PhantomDb.PDBHelper.Factory.Consultar = new PhantomDb.DataSet_String(Datos.ConsultarEx);
                }
                return PhantomDb.PDBHelper.Instance;
            }
        }

        /// <summary>
        /// Devuelve el string connection de la base de datos. En caso de especificarle un nombre erróneo,
        /// devuelve un string connection vacío.
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public static string GetConnectionString(string db)
        {
            switch (db)
            {
                case Names.DbSisContTrans:
                case Names.DbSisCont:
                    return connectionString;
                default:
                    return "";
            }
        }

        /// <summary>
        /// Obtiene el string connection de la base de datos. [DEPRECATED]
        /// Reeemplazar por método "GetConnectionString".
        /// </summary>
        public static string ConnectionString
        {
            get { return Datos.connectionString; }
            set { Datos.connectionString = value; }
        }

        /// <summary>
        /// Mejora una consulta.
        /// </summary>
        /// <param name="consulta"></param>
        private static void MejorarConsulta(ref string consulta)
        {
            if (!consulta.EndsWith(";"))
                consulta = consulta + ";";
        }


        /// <summary>
        /// Realiza una consulta "sin tener en cuenta" las excepciones. Es decir,
        /// captura una excepción, cierra la conexión de manera normal, y luego
        /// la arroja.
        /// </summary>
        /// <param name="consulta"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static DataSet ConectarConsultar(string consulta, string connectionString)
        {
            MejorarConsulta(ref consulta);
#if DEBUG
            System.Diagnostics.Debug.WriteLine(connectionString);
            System.Diagnostics.Debug.WriteLine(consulta);
#endif
            MySqlConnection cn = null;
            try
            {
                cn = new MySqlConnection(connectionString);
                MySqlDataAdapter da = new MySqlDataAdapter(consulta, cn);
                DataSet ds = new DataSet();
                da.Fill(ds);
                cn.Close();
                cn.Dispose();

                return ds;
            }
            catch (Exception ex)
            {
                if (cn != null)
                {
                    cn.Close();
                    cn.Dispose();
                }

                throw ex;
            }
        }

#region TRANSACCIONES

        private static MySqlConnection conexionTransaccional = null;
        private static MySqlTransaction transaccion = null;
        private static bool retorna = false;
        public static object Retorno = null;

        public static void Commit()
        {
            if(transaccion != null)
                transaccion.Commit();
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Commit");
#endif
        }

        public static void Rollback()
        {
            if (transaccion != null)
                transaccion.Rollback();
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Rollback");
#endif
        }

        public static void Release()
        {
            try
            {
                if (conexionTransaccional != null)
                {
                    conexionTransaccional.Close();
                    conexionTransaccional.Dispose();
                }
                if (transaccion != null)
                {
                    transaccion.Dispose();
                    transaccion = null;
                }
            }
            catch
            {

            }
            finally
            {
                conexionTransaccional = null;
                transaccion = null;
            }
        }

        public static void PrepararTransaccion(bool retorno)
        {
            retorna = retorno;
        }
        /// <summary>
        /// la arroja.
        /// </summary>
        /// <param name="consulta"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static DataSet Transaccion(string consulta, string connectionString)
        {
            MejorarConsulta(ref consulta);
#if DEBUG
            System.Diagnostics.Debug.WriteLine(connectionString);
            System.Diagnostics.Debug.WriteLine(consulta);
#endif
            if (conexionTransaccional == null)
            {
                conexionTransaccional = new MySqlConnection(connectionString);
            }

            if (conexionTransaccional.State != ConnectionState.Open)
                conexionTransaccional.Open();

            MySqlConnection cn = conexionTransaccional;

            if(transaccion == null)
                transaccion = cn.BeginTransaction();

            MySqlCommand command = new MySqlCommand(consulta, cn, transaccion);
            if(retorna)
            {
                Retorno =  command.ExecuteScalar();
            }
            else
            {
                command.ExecuteNonQuery();
                Retorno = null;
            }
            return null;
        }


#endregion

        /// <summary>
        /// Realiza una consulta "sin tener en cuenta" las excepciones. Es decir,
        /// captura una excepción, cierra la conexión de manera normal, y luego
        /// la arroja.
        /// </summary>
        /// <param name="consulta"></param>
        /// <returns></returns>
        public static DataSet ConsultarEx(string consulta)
        {
            MejorarConsulta(ref consulta);
            MySqlConnection cn = null;
            try
            {
                cn = new MySqlConnection(connectionString);
                MySqlDataAdapter da = new MySqlDataAdapter(consulta, cn);
                DataSet ds = new DataSet();
                da.Fill(ds);
                cn.Close();

                return ds;
            }
            catch (Exception ex)
            {
                if (cn != null)
                    cn.Close();

                throw ex;
            }
        }
        public static DataSet Consultar(string consulta)
        {
            MejorarConsulta(ref consulta);
            MySqlConnection cn = null;
            DataSet ds = new DataSet();
            try
            {
                cn = new MySqlConnection(connectionString);
                MySqlDataAdapter da = new MySqlDataAdapter(consulta, cn);
                da.Fill(ds);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.StackTrace);
            }

            if (cn != null)
                cn.Close();

            return ds;
        }

       /// <summary>
       /// Chequea si el dataset esta vacía o no.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static bool EstaVacio(DataSet s)
       {
           if (s.Tables.Count == 0)
               return true;
           return s.Tables[0].Rows.Count == 0;
       }

       /// <summary>
       /// Obtiene el primer valor de la primera fila.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static object GetValor(DataSet s)
       {
           return s.Tables[0].Rows[0].ItemArray[0];
       }

       /// <summary>
       /// Obtiene los primeros valores de la primera fila.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static object[] GetValores(DataSet s)
       {
           return s.Tables[0].Rows[0].ItemArray;
       }

       /// <summary>
       /// Obtiene una lista con todos los valores de cada fila.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static List<object[]> GetTotalValores(DataSet s)
       {
           List<object[]> objetos = new List<object[]>();

           foreach (DataRow dr in s.Tables[0].Rows)
           {
               objetos.Add(dr.ItemArray);
           }

           return objetos;
       }

       /// <summary>
       /// Obtiene una lista con todos los valores de cada fila.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static object[] GetTotalValores(DataSet s, int columna)
       {
           object[] objetos = new object[s.Tables[0].Rows.Count];

           for (int i = 0; i < s.Tables[0].Rows.Count; i++)
           {
               objetos[i] = s.Tables[0].Rows[i].ItemArray[columna];
           }

           return objetos;
       }

       public static IList<string> DataSetToString(DataSet ds)
       {
           return DataSetToString(ds, " - ");
       }

       /// <summary>
       /// Obtiene una lista de strings con todos los valores de cada fila.
       /// </summary>
       /// <param name="s"></param>
       /// <returns></returns>
       public static IList<string> DataSetToString(DataSet ds, string separator)
       {
           List<object[]> valores = Datos.GetTotalValores(ds);
           List<string> retorno = new List<string>();
           foreach (object[] obs in valores)
           {
               try
               {
                   string linea = obs[0].ToString();
                   for (int i = 1; i < obs.Length; i++)
                   {
                       linea += separator;
                       linea += obs[i].ToString();
                   }
                   retorno.Add(linea);
               }
               catch { }
           }

           return retorno;
       }


    }//clase
}//namespace
